home *** CD-ROM | disk | FTP | other *** search
/ The Datafile PD-CD 1 Issue 2 / PDCD-1 - Issue 02.iso / _utilities / utilities / 003 / _gs / !GS / c / ISAVE < prev    next >
Text File  |  1991-10-26  |  12KB  |  391 lines

  1. /* Copyright (C) 1991 Aladdin Enterprises.  All rights reserved.
  2.    Distributed by Free Software Foundation, Inc.
  3.  
  4. This file is part of Ghostscript.
  5.  
  6. Ghostscript is distributed in the hope that it will be useful, but
  7. WITHOUT ANY WARRANTY.  No author or distributor accepts responsibility
  8. to anyone for the consequences of using it or for whether it serves any
  9. particular purpose or works at all, unless he says so in writing.  Refer
  10. to the Ghostscript General Public License for full details.
  11.  
  12. Everyone is granted permission to copy, modify and redistribute
  13. Ghostscript, but only under the conditions described in the Ghostscript
  14. General Public License.  A copy of this license is supposed to have been
  15. given to you along with Ghostscript so you can know your rights and
  16. responsibilities.  It should be in a file named COPYING.  Among other
  17. things, the copyright notice and this notice must be preserved on all
  18. copies.  */
  19.  
  20. /* isave.c */
  21. /* Save/restore machinery for Ghostscript */
  22. #include "ghost.h"
  23. #include "memory_.h"
  24. #include "alloc.h"
  25. #include "astate.h"
  26. #include "name.h"
  27. #include "save.h"
  28. #include "store.h"            /* for ref_assign */
  29.  
  30. /* Imported restore routines */
  31. extern void file_restore(P1(alloc_save *));
  32. extern void font_restore(P1(alloc_save *));
  33.  
  34. /*
  35.  * The logic for saving and restore the state is rather subtle.
  36.  * Both the changes to individual objects, and the overall state
  37.  * of the memory manager, must be saved and restored.
  38.  */
  39.  
  40. /*
  41.  * To save the state of the memory manager:
  42.  *    Save the state of the current chunk in which we are allocating.
  43.  *    Save the identity of the current chunk.
  44.  *    Save and reset the malloc chain and the orphan block chains.
  45.  * By doing this, we guarantee that no object older than the save
  46.  * can be freed.
  47.  *
  48.  * To restore the state of the memory manager:
  49.  *    Free all chunks newer than the save.
  50.  *    Free all malloc'ed blocks newer than the save.
  51.  *    Make current the chunk that was current at the time of the save.
  52.  *    Free all objects allocated in the current chunk since the save.
  53.  */
  54.  
  55. /*
  56.  * For saving changes to individual objects, we add an "attribute" bit
  57.  * (l_new) that logically belongs to the slot where the descriptor is stored,
  58.  * not to the descriptor itself.  The bit means "the contents
  59.  * of this slot have been changed since the last save."
  60.  * To keep track of changes since the save, we associate a chain of
  61.  * <slot, old_contents> pairs that remembers the old contents of slots.
  62.  *
  63.  * When creating an object, if the save level is non-zero:
  64.  *    Set the bit in all slots.
  65.  *
  66.  * When storing into a slot, if the save level is non-zero:
  67.  *    If the bit isn't set, save the address and contents of the slot
  68.  *      on the current contents chain.
  69.  *    Set the bit after storing the new value.
  70.  *
  71.  * To do a save:
  72.  *    If the save level is non-zero:
  73.  *        Reset the bit in all slots on the contents chain, and in all
  74.  *          objects created since the previous save.
  75.  *    Push the head of contents chain, and reset the chain to empty.
  76.  *
  77.  * To do a restore:
  78.  *    Check all the stacks to make sure they don't contain references
  79.  *      to objects created since the save.
  80.  *    Restore all the slots on the contents chain.
  81.  *    Pop the contents chain head.
  82.  *    If the save level is now non-zero:
  83.  *        Scan the newly restored contents chain, and set the bit in all
  84.  *          the slots it references.
  85.  *        Scan all objects created since the previous save, and set the
  86.  *          bit in all the slots of each object.
  87.  */
  88.  
  89. /* Declare the mask for checking stores. */
  90. /* This is -1 if we are not in a save, 0 if we are in a save. */
  91. int alloc_save_test_mask;
  92. /* Declare the mask for tagging new objects. */
  93. /* This is 0 if we are not in a save, l_new if we are in a save. */
  94. int alloc_save_new_mask;
  95. #define set_in_save()\
  96.   (alloc_save_test_mask = 0, alloc_save_new_mask = l_new)
  97. #define set_not_in_save()\
  98.   (alloc_save_test_mask = -1, alloc_save_new_mask = 0)
  99.  
  100. /* Structure for saved change chain for save/restore. */
  101. /* If where = 0, contents is a t_array that refers to */
  102. /* a newly allocated object. */
  103. struct alloc_change_s {
  104.     alloc_change *next;
  105.     ref *where;
  106.     ref contents;
  107. };
  108.  
  109. /* Saved state of allocator and other things as needed. */
  110. struct alloc_save_s {
  111.     alloc_state state;
  112.     alloc_state_ptr cap;
  113.     ushort name_count;
  114. };
  115.  
  116. /* Debugging printout */
  117. #ifdef DEBUG
  118. private void
  119. alloc_save_print(alloc_change *cp)
  120. {    dprintf1(" %lx:", (ulong)cp);
  121.     if ( cp->where )
  122.       dprintf4(" %lx: %x %x %lx\n", (ulong)cp->where,
  123.            r_type_attrs(&cp->contents), r_size(&cp->contents),
  124.            (ulong)cp->contents.value.intval);
  125.     else
  126.       dprintf2(" %lx(%u)\n", (ulong)cp->contents.value.refs,
  127.            r_size(&cp->contents));
  128. }
  129. #endif
  130.  
  131. /* Forward references */
  132. private void save_set_new(P2(alloc_state_ptr, int));
  133.  
  134. /* Initialize the save/restore machinery. */
  135. void
  136. alloc_save_init()
  137. {    set_not_in_save();
  138. }
  139.  
  140. /* Save the state. */
  141. alloc_save *
  142. alloc_save_state()
  143. {    register alloc_state_ptr ap = alloc_state_current;
  144.     alloc_save *save =
  145.         (alloc_save *)alloc(1, sizeof(alloc_save),
  146.                     "alloc_save_state");
  147.     if ( save == 0 ) return 0;
  148.     save->state = *ap;
  149.     save->cap = ap;
  150.     save->name_count = name_next_index();
  151.     /* Reset the l_new attribute in all slots.  The only slots that */
  152.     /* can have the attribute set are the ones on the changes chain. */
  153.     save_set_new(ap, 0);
  154.     /* Clear the free chains, to prevent old objects from being freed. */
  155.     memset(&ap->free[0], 0, num_free_chains * sizeof(char *));
  156.     ap->malloc_chain = 0;
  157.     ap->saved = save;
  158.     ap->save_level++;
  159.     ap->changes = 0;
  160.     ap->saved_cbot = ap->cbot;
  161.     ap->saved_ctop = ap->ctop;
  162.     set_in_save();
  163.     return save;
  164. }
  165.  
  166. /* Allocate a ref-containing object and record it as new. */
  167. ref *
  168. alloc_refs(uint num_refs, char *client_name)
  169. {    register alloc_state_ptr ap = alloc_state_current;
  170.     register alloc_change *cp;
  171.     register ref *obj = (ref *)alloc(num_refs, sizeof(ref), client_name);
  172.     if ( obj == 0 ) return 0;
  173.     if ( ap->save_level == 0 ) /* no saving */
  174.       return obj;
  175.     cp = (alloc_change *)alloc(1, sizeof(alloc_change),
  176.                    "alloc_refs");
  177.     if ( cp == 0 )
  178.        {    alloc_free((char *)obj, num_refs, sizeof(ref), client_name);
  179.         return 0;
  180.        }
  181.     cp->next = ap->changes;
  182.     cp->where = 0;
  183.     r_set_size(&cp->contents, num_refs);
  184.     cp->contents.value.refs = obj;
  185. #ifdef DEBUG
  186. if ( gs_debug['u'] )
  187.    {    dprintf1("[u]alloc_refs %s", client_name);
  188.     alloc_save_print(cp);
  189.    }
  190. #endif
  191.     ap->changes = cp;
  192.     return obj;
  193. }
  194.  
  195.  
  196. /* Record a state change that must be undone for restore, */
  197. /* and mark it as having been saved. */
  198. /* This can only be called if we are in a save. */
  199. int
  200. alloc_save_change(ref *where, char *client_name)
  201. {    register alloc_state_ptr ap = alloc_state_current;
  202.     register alloc_change *cp;
  203.     if ( ap->save_level == 0 ) return 0;    /* no saving */
  204.     cp = (alloc_change *)alloc(1, sizeof(alloc_change),
  205.                    "alloc_save_change");
  206.     if ( cp == 0 ) return -1;
  207.     cp->next = ap->changes;
  208.     cp->where = where;
  209.     ref_assign(&cp->contents, where);
  210. #ifdef DEBUG
  211. if ( gs_debug['u'] )
  212.    {    dprintf1("[u]save %s", client_name);
  213.     alloc_save_print(cp);
  214.    }
  215. #endif
  216.     ap->changes = cp;
  217.     r_set_attrs(where, l_new);
  218.     return 0;
  219. }
  220.  
  221. /* Return the current save level */
  222. int
  223. alloc_save_level()
  224. {    return alloc_state_current->save_level;
  225. }
  226.  
  227. /* Test whether a reference would be invalidated by a restore. */
  228. int
  229. alloc_is_since_save(char *ptr, alloc_save *save)
  230. {
  231.     /* A reference can postdate a save in one of three ways: */
  232.     /*    - It is in the chunk that was current at the time */
  233.     /*        of the save, and allocated more recently. */
  234.     /*    - It is in a chunk allocated since the save; */
  235.     /*    - It was malloc'ed since the save; */
  236.  
  237.     register alloc_state_ptr ap = save->cap;
  238.  
  239.     /* Check against current chunk at the time of the save */
  240.     if ( ptr_is_in_chunk(ptr, &save->state.current) )
  241.        {    /* In the chunk, check against allocation pointers */
  242.         /* at the time of the save */
  243.         return ( (ptr_ord_t)ptr >= (ptr_ord_t)save->state.cbot &&
  244.              (ptr_ord_t)ptr < (ptr_ord_t)save->state.ctop );
  245.        }
  246.  
  247.     /* Check against chunks allocated since the save */
  248.        {    alloc_chunk *chunk = &ap->current;
  249.         while ( chunk->save_level > save->state.save_level )
  250.            {    if ( ptr_is_in_chunk(ptr, chunk) ) return 1;
  251.             chunk = chunk->next;
  252.            }
  253.        }
  254.  
  255.     /* Check the malloc chains since the save */
  256.        {    alloc_state *asp = ap;
  257.         for ( ; asp != &save->state; asp = &asp->saved->state )
  258.            {    alloc_block *mblk = asp->malloc_chain;
  259.             for ( ; mblk != 0; mblk = mblk->next )
  260.               if ( alloc_block_size + (char *)mblk == ptr ) return 1;
  261.            }
  262.        }
  263.  
  264.     /* Not in any of those places, must be OK. */
  265.     return 0;
  266. }
  267.  
  268. /* Test whether a name would be invalidated by a restore. */
  269. int
  270. alloc_name_is_since_save(ref *pnref, alloc_save *save)
  271. {    return pnref->value.pname->index >= save->name_count;
  272. }
  273.  
  274. /* Validate a saved state pointer. */
  275. int
  276. alloc_restore_state_check(alloc_save *save)
  277. {    alloc_save *sprev = save->cap->saved;
  278.     while ( sprev != save )
  279.        {    if ( sprev == 0 ) return -1;    /* not on chain */
  280.         sprev = sprev->state.saved;
  281.        }
  282.     return 0;
  283. }
  284.  
  285. /* Restore the state.  The client is responsible for calling */
  286. /* alloc_restore_state_check first, and for ensuring that */
  287. /* there are no surviving pointers for which alloc_is_since_save is true. */
  288. void
  289. alloc_restore_state(alloc_save *save)
  290. {    register alloc_state_ptr ap = save->cap;
  291.     alloc_save *sprev;
  292.  
  293.     /* Iteratively restore the state */
  294.     do
  295.       { sprev = ap->saved;
  296.  
  297.         /* Close inaccessible files. */
  298.         file_restore(save);
  299.  
  300.         /* Remove entries from font and character caches. */
  301.         font_restore(save);
  302.  
  303.         /* Adjust the name table. */
  304.         name_restore(sprev->name_count);
  305.  
  306.         /* Undo changes since the save. */
  307.         { alloc_change *cp = ap->changes;
  308.           while ( cp )
  309.         {
  310. #ifdef DEBUG
  311. if ( gs_debug['u'] )
  312.    {    dprintf("[u]restore");
  313.     alloc_save_print(cp);
  314.    }
  315. #endif
  316.           if ( cp->where )
  317.             ref_assign(cp->where, &cp->contents);
  318.           else
  319.             { alloc_free((char *)cp->contents.value.refs,
  320.                  r_size(&cp->contents), sizeof(ref),
  321.                  "alloc_restore_state");
  322.             }
  323.           cp = cp->next;
  324.         }
  325.         }
  326.  
  327.         /* Free chunks allocated since the save. */
  328.         { alloc_chunk *cp = ap->current_ptr;
  329.           *cp = ap->current;    /* update in memory */
  330.         }
  331.         while ( ap->current.save_level == ap->save_level )
  332.           {    byte *cp = (byte *)ap->current_ptr;
  333.         uint csize = ap->climit - cp;
  334.         ap->current_ptr = ap->current.next;
  335.         ap->current = *ap->current_ptr;
  336.         (*ap->pfree)((char *)cp, 1, csize, "alloc_restore_state(chunk)");
  337.           }
  338.  
  339.         /* Free blocks allocated with malloc since the save. */
  340.         /* Since we reset the chain when we did the save, */
  341.         /* we just free all the objects on the current chain. */
  342.         { while ( ap->malloc_chain != 0 )
  343.         { alloc_block *mblock = ap->malloc_chain;
  344.           ap->malloc_chain = mblock->next;
  345.           (*ap->pfree)((char *)mblock,
  346.                    1, alloc_block_size + mblock->size,
  347.                    "alloc_restore_state(malloc'ed)");
  348.         }
  349.         }
  350.  
  351.         /* Restore the allocator state. */
  352.         *ap = sprev->state;
  353.         alloc_free((char *)sprev, 1, sizeof(alloc_save),
  354.                "alloc_restore_state");
  355.  
  356.       }
  357.     while ( sprev != save );
  358.  
  359.     /* Clean up */
  360.     if ( sprev != 0 )
  361.       ap->saved_cbot = sprev->state.cbot,
  362.       ap->saved_ctop = sprev->state.ctop;
  363.     if ( ap->save_level == 0 )
  364.       set_not_in_save();
  365.     /* Set the l_new attribute in all slots that have been saved. */
  366.     save_set_new(ap, l_new);
  367. }
  368.  
  369. /* ------ Internal routines ------ */
  370.  
  371. /* Set or reset the l_new attribute in every slot on the current */
  372. /* change chain. */
  373. private void
  374. save_set_new(alloc_state_ptr ap, int new)        /* l_new or 0 */
  375. {    register alloc_change *cp = ap->changes;
  376.     while ( cp )
  377.        {    if ( cp->where != 0 )
  378.             cp->where->tas.type_attrs =
  379.               (cp->where->tas.type_attrs & ~l_new) + new;
  380.         else
  381.            {    register ushort size = r_size(&cp->contents);
  382.             register ref *ep = cp->contents.value.refs;
  383.             while ( size-- )
  384.                 ep->tas.type_attrs =
  385.                   (ep->tas.type_attrs & ~l_new) + new,
  386.                 ep++;
  387.            }
  388.         cp = cp->next;
  389.        }
  390. }
  391.